home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / imapd / imapd.c < prev    next >
C/C++ Source or Header  |  1997-02-21  |  43KB  |  1,434 lines

  1. /*
  2.  * Program:    IMAP2bis server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 November 1990
  13.  * Last Edited:    21 February 1997
  14.  *
  15.  * Copyright 1997 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Parameter files */
  37.  
  38. #include <sys/time.h>        /* must be before osdep.h */
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <stdio.h>
  42. #include <ctype.h>
  43. #include <netdb.h>
  44. #include <errno.h>
  45. extern int errno;        /* just in case */
  46. #include <signal.h>
  47. #include <pwd.h>
  48. #include <sys/file.h>
  49. #include <sys/stat.h>
  50. #include "misc.h"
  51.  
  52.  
  53. /* Daemon files */
  54. #define ALERTFILE "/etc/imapd.alert"
  55. #define ANOFILE "/etc/anonymous.newsgroups"
  56.  
  57.  
  58. /* Autologout timer */
  59. #define TIMEOUT 60*30
  60.  
  61.  
  62. /* Login tries */
  63. #define LOGTRY 3
  64.  
  65.  
  66. /* Size of temporary buffers */
  67. #define TMPLEN 8192
  68.  
  69.  
  70. /* Server states */
  71.  
  72. #define LOGIN 0
  73. #define SELECT 1
  74. #define OPEN 2
  75. #define LOGOUT 3
  76.  
  77. /* Global storage */
  78.  
  79. char *version = "7.8(100)";    /* version number of this server */
  80. time_t alerttime = 0;        /* time of last alert */
  81. int state = LOGIN;        /* server state */
  82. int mackludge = 0;        /* MacMS kludge */
  83. int trycreate = 0;        /* saw a trycreate */
  84. int anonymous = 0;        /* non-zero if anonymous */
  85. int logtry = LOGTRY;        /* login tries */
  86. long kodcount = 0;        /* set if KOD has happened already */
  87. MAILSTREAM *stream = NIL;    /* mailbox stream */
  88. MAILSTREAM *tstream = NIL;    /* temporary mailbox stream */
  89. long nmsgs = 0;            /* number of messages */
  90. long recent = 0;        /* number of recent messages */
  91. char *host = NIL;        /* local host name */
  92. char *user = NIL;        /* user name */
  93. char *pass = NIL;        /* password */
  94. char *home = NIL;        /* home directory */
  95. char *flags = NIL;        /* flag text */
  96. char cmdbuf[TMPLEN];        /* command buffer */
  97. char *tag;            /* tag portion of command */
  98. char *cmd;            /* command portion of command */
  99. char *arg;            /* pointer to current argument of command */
  100. char *lsterr = NIL;        /* last error message from c-client */
  101. char *response = NIL;        /* command response */
  102. char *litbuf = NIL;        /* buffer to hold literals */
  103.  
  104.  
  105. /* Response texts */
  106.  
  107. char *win = "%s OK %s completed\015\012";
  108. char *altwin = "%s OK %s\015\012";
  109. char *lose = "%s NO %s failed: %s\015\012";
  110. char *misarg = "%s BAD Missing required argument to %s\015\012";
  111. char *badfnd = "%s BAD FIND option unrecognized: %s\015\012";
  112. char *badarg = "%s BAD Argument given to %s when none expected\015\012";
  113. char *badseq = "%s BAD Bogus sequence in %s\015\012";
  114. char *badatt = "%s BAD Bogus attribute list in %s\015\012";
  115. char *badlit = "%s BAD Bogus literal count in %s\015\012";
  116. char *toobig = "%s BAD Command line too long\015\012";
  117. char *nulcmd = "* BAD Null command\015\012";
  118. char *argrdy = "+ Ready for argument\015\012";
  119.  
  120. /* Function prototypes */
  121.  
  122. void main (int argc,char *argv[]);
  123. void clkint ();
  124. void kodint ();
  125. void slurp (char *s,int n);
  126. char inchar ();
  127. void *flush ();
  128. char *snarf (char **arg);
  129. void fetch (char *s,char *t);
  130. void fetch_body (long i,char *s);
  131. void fetch_body_part (long i,char *s);
  132. void fetch_envelope (long i,char *s);
  133. void fetch_encoding (long i,char *s);
  134. void changed_flags (long i,int f);
  135. void fetch_flags (long i,char *s);
  136. void fetch_internaldate (long i,char *s);
  137. void fetch_rfc822 (long i,char *s);
  138. void fetch_rfc822_header (long i,char *s);
  139. void fetch_rfc822_size (long i,char *s);
  140. void fetch_rfc822_text (long i,char *s);
  141. void penv (ENVELOPE *env);
  142. void pbody (BODY *body);
  143. void pstring (char *s);
  144. void paddr (ADDRESS *a);
  145. long cstring (char *s);
  146. long caddr (ADDRESS *a);
  147.  
  148. /* Main program */
  149.  
  150. void main (int argc,char *argv[])
  151. {
  152.   long i;
  153.   char *s,*t,*u,*v,tmp[MAILTMPLEN];
  154.   struct hostent *hst;
  155.   struct stat sbuf;
  156.   void (*f) () = NIL;
  157. #include "linkage.c"
  158.   openlog ("imapd",LOG_PID,LOG_MAIL);
  159.   gethostname (cmdbuf,TMPLEN-1);/* get local name */
  160.   host = cpystr ((hst = gethostbyname (cmdbuf)) ? hst->h_name : cmdbuf);
  161.   rfc822_date (cmdbuf);        /* get date/time now */
  162.   if (i = getuid ()) {        /* logged in? */
  163.     if (!(s = (char *) getlogin ())) s = (getpwuid (i))->pw_name;
  164.     if (i < 0) anonymous = T;    /* must be anonymous if pseudo-user */
  165.     user = cpystr (s);        /* set up user name */
  166.     pass = cpystr ("*");    /* and fake password */
  167.     state = SELECT;        /* enter select state */
  168.     t = "PREAUTH";        /* pre-authorized */
  169.     syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",user,
  170.         tcp_clienthost (tmp));
  171.   }
  172.   else t = "OK";
  173.   printf ("* %s %s IMAP2bis Service %s at %s\015\012",t,host,version,cmdbuf);
  174.   fflush (stdout);        /* dump output buffer */
  175.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  176.   signal (SIGUSR2,kodint);    /* prepare for Kiss Of Death */
  177.   do {                /* command processing loop */
  178.     slurp (cmdbuf,TMPLEN-1);    /* slurp command */
  179.                 /* no more last error or literal */
  180.     if (lsterr) fs_give ((void **) &lsterr);
  181.     if (litbuf) fs_give ((void **) &litbuf);
  182.                 /* find end of line */
  183.     if (!strchr (cmdbuf,'\012')) {
  184.       if (t = strchr (cmdbuf,' ')) *t = '\0';
  185.       flush ();            /* flush excess */
  186.       printf (response,t ? cmdbuf : "*");
  187.     }
  188.     else if (!(tag = strtok (cmdbuf," \015\012"))) fputs (nulcmd,stdout);
  189.     else if (!(cmd = strtok (NIL," \015\012")))
  190.       printf ("%s BAD Missing command\015\012",tag);
  191.     else {            /* parse command */
  192.       response = win;        /* set default response */
  193.       ucase (cmd);        /* canonicalize command case */
  194.                 /* snarf argument */
  195.       arg = strtok (NIL,"\015\012");
  196.                 /* LOGOUT command always valid */
  197.       if (!strcmp (cmd,"LOGOUT")) {
  198.     if (state == OPEN) mail_close (stream);
  199.     stream = NIL;
  200.     printf("* BYE %s IMAP2bis server terminating connection\015\012",host);
  201.     state = LOGOUT;
  202.       }
  203.  
  204.                 /* kludge for MacMS */
  205.       else if ((!strcmp (cmd,"VERSION")) && (s = snarf (&arg)) && (!arg) &&
  206.            (i = atol (s)) && (i == 4)) {
  207.     mackludge = T;
  208.     fputs ("* OK [MacMS] The MacMS kludges are enabled\015\012",stdout);
  209.       }
  210.       else if (!strcmp (cmd,"NOOP")) {
  211.     if ((state == OPEN) && !mail_ping (stream)) {
  212.       printf ("* BYE %s Fatal mailbox error: %s\015\012",host,
  213.           lsterr ? lsterr : "<unknown>");
  214.       state = LOGOUT;    /* go away */
  215.       syslog (LOG_INFO,
  216.           "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
  217.           user ? user : "???",tcp_clienthost (tmp),
  218.           (stream && stream->mailbox) ? stream->mailbox : "???",
  219.           lsterr ? lsterr : "???");
  220.     }
  221.       }
  222.       else switch (state) {    /* dispatch depending upon state */
  223.       case LOGIN:        /* waiting to get logged in */
  224.     if (!strcmp (cmd,"LOGIN")) {
  225.       struct passwd *pwd;
  226.       if (user) fs_give ((void **) &user);
  227.       if (pass) fs_give ((void **) &pass);
  228.                 /* two arguments */
  229.       if (!((user = cpystr (snarf (&arg))) &&
  230.         (pass = cpystr (snarf (&arg))))) response = misarg;
  231.       else if (arg) response = badarg;
  232.                 /* see if username and password are OK */
  233.       else if (((i = strlen (user)) < 64) &&
  234.            server_login (user,pass,&home,argc,argv)) {
  235.         state = SELECT;
  236.         syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
  237.             tcp_clienthost(tmp));
  238.       }
  239.                 /* nope, see if we allow anonymous */
  240.       else if (!stat (ANOFILE,&sbuf) && !strcmp (user,"anonymous") &&
  241.            (pwd = getpwnam ("nobody"))) {
  242.         anonymous = T;    /* note we are anonymous, login as nobody */
  243.         setgid (pwd->pw_gid);
  244.         setuid (pwd->pw_uid);
  245.         state = SELECT;    /* make selected */
  246.         syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
  247.             tcp_clienthost(tmp));
  248.       }
  249.       else {
  250.         if (i > 128)
  251.           syslog (LOG_ALERT|LOG_AUTH,"Crack attempt, host=%.80s",
  252.               tcp_clienthost (tmp));
  253.         response = "%s NO Bad %s user name and/or password\015\012";
  254.         sleep (3);        /* slow the cracker down */
  255.         if (!--logtry) {
  256.           fputs ("* BYE Too many login failures\015\012",stdout);
  257.           state = LOGOUT;
  258.           syslog(LOG_INFO,"Excessive login failures user=%.80s host=%.80s",
  259.              user,tcp_clienthost (tmp));
  260.         }
  261.         else syslog (LOG_INFO,"Login failure user=%.80s host=%.80s",
  262.              user,tcp_clienthost (tmp));
  263.       }
  264.     }
  265.     else response = "%s BAD Command unrecognized/login please: %s\015\012";
  266.     break;
  267.  
  268.       case OPEN:        /* valid only when mailbox open */
  269.                 /* fetch mailbox attributes */
  270.     if (!strcmp (cmd,"FETCH")) {
  271.       if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
  272.         response = misarg;
  273.       else fetch (s,t);    /* do the fetch */
  274.     }
  275.                 /* fetch partial mailbox attributes */
  276.     else if (!strcmp (cmd,"PARTIAL")) {
  277.       unsigned long msgno,start,count,size;
  278.       if (!(arg && (msgno = strtol (arg,&s,10)) && (t = strtok (s," ")) &&
  279.         (s = strtok (NIL,"\015\012")) && (start = strtol (s,&s,10)) &&
  280.         (count = strtol (s,&s,10)))) response = misarg;
  281.       else if (s && *s) response = badarg;
  282.       else if (msgno > stream->nmsgs) response = badseq;
  283.       else {        /* looks good */
  284.         int f = mail_elt (stream,msgno)->seen;
  285.         u = s = NIL;    /* no strings yet */
  286.         if (!strcmp (ucase (t),"RFC822")) {
  287.                 /* have to make a temporary buffer for this */
  288.           size = mail_elt (stream,msgno)->rfc822_size;
  289.           s = u = (char *) fs_get (size + 1);
  290.           strcpy (u,mail_fetchheader (stream,msgno));
  291.           strcat (u,mail_fetchtext (stream,msgno));
  292.           u[size] = '\0';    /* tie off string */
  293.         }
  294.         else if (!strcmp (t,"RFC822.HEADER"))
  295.           size = strlen (s = mail_fetchheader (stream,msgno));
  296.         else if (!strcmp (t,"RFC822.TEXT"))
  297.           size = strlen (s = mail_fetchtext (stream,msgno));
  298.         else if (*t == 'B' && t[1] == 'O' && t[2] == 'D' && t[3] == 'Y' &&
  299.              t[4] == '[' && *(t += 5)) {
  300.           if ((v = strchr (t,']')) && !v[1]) {
  301.         *v = '\0';    /* tie off body part */
  302.         s = mail_fetchbody (stream,msgno,t,&size);
  303.           }
  304.         }
  305.         if (s) {        /* got a string back? */
  306.           if (size <= --start) s = NIL;
  307.           else {        /* have string we can make smaller */
  308.         s += start;    /* this is the first byte */
  309.                 /* tie off as appropriate */
  310.         if (count < (size - start)) s[count] = '\0';
  311.           }
  312.           printf ("* %ld FETCH (%s ",msgno,t);
  313.           pstring (s);    /* write the string */
  314.           changed_flags (msgno,f);
  315.           fputs (")\015\012",stdout);
  316.           if (u) fs_give ((void **) &u);
  317.         }
  318.         else response = badatt;
  319.       }
  320.     }
  321.  
  322.                 /* store mailbox attributes */
  323.     else if (!strcmp (cmd,"STORE")) {
  324.                 /* must have three arguments */
  325.       if (!(arg && (s = strtok (arg," ")) && (cmd = strtok (NIL," ")) &&
  326.         (t = strtok (NIL,"\015\012")))) response = misarg;
  327.       else if (!strcmp (ucase (cmd),"+FLAGS")) f = mail_setflag;
  328.       else if (!strcmp (cmd,"-FLAGS")) f = mail_clearflag;
  329.       else if (!strcmp (cmd,"FLAGS")) {
  330.         mail_clearflag (stream,s,flags);
  331.         f = mail_setflag;    /* gross, but rarely if ever done */
  332.       }
  333.       else response = "%s BAD STORE %s not yet implemented\015\012";
  334.       if (f) {        /* if a function was selected */
  335.         (*f) (stream,s,t);    /* do it */
  336.         fetch (s,"FLAGS");    /* get the new flags status */
  337.       }
  338.     }
  339.                 /* check for new mail */
  340.     else if (!strcmp (cmd,"CHECK")) {
  341.                 /* no arguments */
  342.       if (arg) response = badarg;
  343.       else if (anonymous) mail_ping (stream);
  344.       else mail_check (stream);
  345.     }
  346.                 /* expunge deleted messages */
  347.     else if (!(anonymous || strcmp (cmd,"EXPUNGE"))) {
  348.                 /* no arguments */
  349.       if (arg) response = badarg;
  350.       else mail_expunge (stream);
  351.     }
  352.                 /* copy message(s) */
  353.     else if (!(anonymous || strcmp (cmd,"COPY"))) {
  354.       trycreate = NIL;    /* no trycreate status */
  355.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  356.       else if (arg) response = badarg;
  357.       else if ((!mail_copy (stream,s,t)) &&
  358.            !(mackludge && trycreate &&
  359.              (mail_create (stream,t)) && (mail_copy (stream,s,t)))) {
  360.         response = lose;    /* in case TRYCREATE failure */
  361.         if (!lsterr) lsterr = cpystr ("No such destination mailbox");
  362.       }
  363.     }
  364.  
  365.                 /* search mailbox */
  366.     else if (!strcmp (cmd,"SEARCH")) {
  367.                 /* one or more arguments required */
  368.       if (!arg) response = misarg;
  369.       else {        /* zap search vector */
  370.                 /* end with a literal argument? */
  371.         while ((*(t = arg + strlen (arg) - 1) == '}') &&
  372.            (s = strrchr (arg,'{')) && response == win) {
  373.                 /* get length of literal, validate */
  374.           if (((i = atol (++s)) < 1) || (TMPLEN - (t++ - cmdbuf) < i + 10))
  375.         response = badlit;
  376.           else {
  377.         *t++ = '\015';    /* reappend CR/LF */
  378.         *t++ = '\012';
  379.         fputs (argrdy,stdout);
  380.         fflush (stdout);/* dump output buffer */
  381.                 /* copy the literal */
  382.         while (i--) *t++ = inchar ();
  383.                 /* get new command tail */
  384.         slurp (t,TMPLEN - (t-cmdbuf) - 1);
  385.                 /* find end of line */
  386.         if (s = strchr (t,'\012')) {
  387.           *s = NIL;    /* tie off line */
  388.           if (*--s == '\015') *s = NIL;
  389.         }
  390.         else flush ();    /* error */
  391.           }
  392.         }
  393.                 /* punt if error */
  394.         if (response != win) break;
  395.                 /* do the search */
  396.         mail_search (stream,arg);
  397.         if (response == win || response == altwin) {
  398.                 /* output search results */
  399.           fputs ("* SEARCH",stdout);
  400.           for (i = 1; i <= nmsgs; ++i)
  401.         if (mail_elt (stream,i)->searched) printf (" %d",i);
  402.           fputs ("\015\012",stdout);
  403.         }
  404.       }
  405.     }
  406.     else            /* fall into select case */
  407.  
  408.       case SELECT:        /* valid whenever logged in */
  409.                 /* select new mailbox */
  410.     if ((!(anonymous || (strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE"))))
  411.         || (!strcmp (cmd,"BBOARD"))) {
  412.                 /* single argument */
  413.       if (!(s = snarf (&arg))) response = misarg;
  414.       else if (arg) response = badarg;
  415.       else {
  416.         sprintf (tmp,"%s%s",(*cmd == 'B') ? "*" : "",s);
  417.         recent = -1;    /* make sure we get an update */
  418.         if ((stream = mail_open (stream,tmp,anonymous ? OP_ANONYMOUS : NIL
  419.                      + (*cmd == 'E') ? OP_READONLY : NIL))
  420.         && ((response == win) || (response == altwin))) {
  421.                 /* flush old list */
  422.           if (flags) fs_give ((void **) &flags);
  423.           s = tmp;        /* write flags here */
  424.           *s = '(';        /* start new flag list */
  425.           s[1] = '\0';
  426.           for (i = 0; i < NUSERFLAGS; i++)
  427.         if (t = stream->user_flags[i]) strcat (strcat (s,t)," ");
  428.                 /* append system flags to list */
  429.           strcat (s,"\\Answered \\Flagged \\Deleted \\Seen)");
  430.                 /* output list of flags */
  431.           printf ("* FLAGS %s\015\012",(flags = cpystr (s)));
  432.           kodcount = 0;    /* initialize KOD count */
  433.           state = OPEN;    /* note state open */
  434.                 /* note readonly/readwrite */
  435.           response = stream->rdonly ?
  436.         "%s OK [READ-ONLY] %s completed\015\012" :
  437.           "%s OK [READ-WRITE] %s completed\015\012";
  438.           if (anonymous)
  439.         syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
  440.             stream->mailbox,tcp_clienthost (tmp));
  441.         }
  442.         else {        /* nuke if still open */
  443.           if (stream) mail_close (stream);
  444.           stream = NIL;
  445.           state = SELECT;    /* no mailbox open now */
  446.           response = lose;    /* open failed */
  447.         }
  448.       }
  449.     }
  450.  
  451.                 /* APPEND message to mailbox */
  452.     else if (!(anonymous || strcmp (cmd,"APPEND"))) {
  453.       u = v = NIL;        /* init flags/date */
  454.                 /* parse mailbox name */
  455.       if ((s = snarf (&arg)) && arg) {
  456.         if (*arg == '(') {    /* parse optional flag list */
  457.           u = ++arg;    /* pointer to flag list contents */
  458.           while (*arg && (*arg != ')')) arg++;
  459.           if (*arg) *arg++ = '\0';
  460.           if (*arg == ' ') arg++;
  461.         }
  462.                 /* parse optional date */
  463.         if (*arg == '"') v = snarf (&arg);
  464.                 /* parse message */
  465.         if (!arg || (*arg != '{'))
  466.           response = "%s BAD Missing literal in %s\015\012";
  467.         else if (!(isdigit (arg[1]) && (i = strtol (arg+1,NIL,10))))
  468.           response = "%s NO Empty message to %s\015\012";
  469.         else if (!(t = snarf (&arg))) response = misarg;
  470.         else if (arg) response = badarg;
  471.         else {        /* append the data */
  472.           STRING st;
  473.           INIT (&st,mail_string,(void *) t,i);
  474.           if (!mail_append_full (NIL,s,u,v,&st)) {
  475.         response = lose;/* in case TRYCREATE failure */
  476.         if (!lsterr) lsterr = cpystr ("No such destination mailbox");
  477.           }
  478.         }
  479.       }
  480.       else response = misarg;
  481.     }
  482.  
  483.                 /* find mailboxes or bboards */
  484.     else if (!strcmp (cmd,"FIND")) {
  485.       response = "%s OK FIND %s completed\015\012";
  486.                 /* get subcommand and true argument */
  487.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  488.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  489.         response = misarg;    /* missing required argument */
  490.       else if (arg) response = badarg;
  491.       else if (anonymous) {    /* special version for anonymous users */
  492.         if (!strcmp (cmd,"BBOARDS") || !strcmp (cmd,"ALL.BBOARDS"))
  493.           mail_find_all_bboard (NIL,s);
  494.                 /* other commands not supported */
  495.             else response = badfnd;
  496.       }
  497.       else {        /* dispatch based on type */
  498.         if ((!strcmp (cmd,"MAILBOXES"))||(!strcmp (cmd,"ALL.MAILBOXES"))) {
  499.           if (*s == '{') tstream = mail_open (NIL,s,OP_HALFOPEN);
  500.           if (*cmd == 'A') { /* want them all? */
  501.         mail_find_all (NIL,s);
  502.         if (tstream) mail_find_all (tstream,s);
  503.           }
  504.           else {        /* just subscribed */
  505.         mail_find (NIL,s);
  506.         if (tstream) mail_find (tstream,s);
  507.           }
  508.         }
  509.         else if ((!strcmp (cmd,"BBOARDS"))||(!strcmp (cmd,"ALL.BBOARDS"))){
  510.           if (*s == '{') {    /* prepend leading * if remote */
  511.         sprintf (tmp,"*%s",s);
  512.         tstream = mail_open (NIL,tmp,OP_HALFOPEN);
  513.           }
  514.           if (*cmd == 'A') { /* want them all? */
  515.         mail_find_all_bboard (NIL,s);
  516.         if (tstream) mail_find_all_bboard (tstream,s);
  517.           }
  518.           else {        /* just subscribed */
  519.         mail_find_bboards (NIL,s);
  520.         if (tstream) mail_find_bboards (tstream,s);
  521.           }
  522.         }
  523.         else response = badfnd;
  524.         if (tstream) mail_close (tstream);
  525.         tstream = NIL;    /* no more temporary stream */
  526.       }
  527.     }
  528.  
  529.                 /* subscribe to mailbox or bboard */
  530.     else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
  531.       response = "%s OK SUBSCRIBE %s completed\015\012";
  532.                 /* get subcommand and true argument */
  533.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  534.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  535.         response = misarg;    /* missing required argument */
  536.       else if (arg) response = badarg;
  537.       else {        /* dispatch based on type */
  538.         if (!strcmp (cmd,"MAILBOX")) mail_subscribe (NIL,s);
  539.         else if (!strcmp (cmd,"BBOARD")) mail_subscribe_bboard (NIL,s);
  540.         else response = "%s BAD SUBSCRIBE option unrecognized: %s\015\012";
  541.       }
  542.     }
  543.                 /* unsubscribe to mailbox or bboard */
  544.     else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
  545.       response = "%s OK UNSUBSCRIBE %s completed\015\012";
  546.                 /* get subcommand and true argument */
  547.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  548.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  549.         response = misarg;    /* missing required argument */
  550.       else if (arg) response = badarg;
  551.       else {        /* dispatch based on type */
  552.         if (!strcmp (cmd,"MAILBOX")) mail_unsubscribe (NIL,s);
  553.         else if (!strcmp (cmd,"BBOARD")) mail_unsubscribe_bboard (NIL,s);
  554.         else response="%s BAD UNSUBSCRIBE option unrecognized: %s\015\012";
  555.       }
  556.     }
  557.                 /* create mailbox */
  558.     else if (!(anonymous || strcmp (cmd,"CREATE"))) {
  559.       if (!(s = snarf (&arg))) response = misarg;
  560.       else if (arg) response = badarg;
  561.       mail_create (NIL,s);    /* create the sucker */
  562.     }
  563.                 /* delete mailbox */
  564.     else if (!(anonymous || strcmp (cmd,"DELETE"))) {
  565.       if (!(s = snarf (&arg))) response = misarg;
  566.       else if (arg) response = badarg;
  567.       else mail_delete (NIL,s);
  568.     }
  569.                 /* rename mailbox */
  570.     else if (!(anonymous || strcmp (cmd,"RENAME"))) {
  571.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  572.       else if (arg) response = badarg;
  573.       else mail_rename (NIL,s,t);
  574.     }
  575.  
  576.     else response = "%s BAD Command unrecognized: %s\015\012";
  577.     break;
  578.       default:
  579.         response = "%s BAD Server in unknown state for %s command\015\012";
  580.     break;
  581.       }
  582.       if (state == OPEN) {    /* mailbox open? */
  583.                 /* yes, change in recent messages? */
  584.     if (recent != stream->recent)
  585.       printf ("* %d RECENT\015\012",(recent = stream->recent));
  586.                 /* output changed flags */
  587.     for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->spare2) {
  588.       printf ("* %d FETCH (",i);
  589.       fetch_flags (i,NIL);
  590.       fputs (")\015\012",stdout);
  591.     }
  592.       }
  593.                 /* output any new alerts */
  594.       if (!stat (ALERTFILE,&sbuf) && (sbuf.st_mtime > alerttime)) {
  595.     FILE *alf = fopen (ALERTFILE,"r");
  596.     int c,lc = '\012';
  597.     if (alf) {        /* only if successful */
  598.       while ((c = getc (alf)) != EOF) {
  599.         if (lc == '\012') fputs ("* OK [ALERT] ",stdout);
  600.         switch (c) {    /* output character */
  601.         case '\012':
  602.           fputs ("\015\012",stdout);
  603.         case '\0':        /* flush nulls */
  604.           break;
  605.         default:
  606.           putchar (c);    /* output all other characters */
  607.           break;
  608.         }
  609.         lc = c;        /* note previous character */
  610.       }
  611.       fclose (alf);
  612.       if (lc != '\012') fputs ("\015\012",stdout);
  613.       alerttime = sbuf.st_mtime;
  614.     }
  615.       }
  616.                 /* get text for alternative win message now */
  617.       if (response == altwin) cmd = lsterr;
  618.                 /* output response */
  619.       printf (response,tag,cmd,lsterr);
  620.     }
  621.     fflush (stdout);        /* make sure output blatted */
  622.   } while (state != LOGOUT);    /* until logged out */
  623.   syslog (LOG_INFO,"Logout user=%.80s host=%.80s",user ? user : "???",
  624.       tcp_clienthost (tmp));
  625.   exit (0);            /* all done */
  626. }
  627.  
  628. /* Clock interrupt
  629.  */
  630.  
  631. void clkint ()
  632. {
  633.   char tmp[MAILTMPLEN];
  634.   fputs ("* BYE Autologout; idle for too long\015\012",stdout);
  635.   syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???",
  636.       tcp_clienthost (tmp));
  637.   fflush (stdout);        /* make sure output blatted */
  638.                 /* try to gracefully close the stream */
  639.   if (state == OPEN) mail_close (stream);
  640.   stream = NIL;
  641.   exit (0);            /* die die die */
  642. }
  643.  
  644.  
  645. /* Kiss Of Death interrupt
  646.  */
  647.  
  648. void kodint ()
  649. {
  650.   char *s;
  651.   if (!kodcount++) {        /* only do this once please */
  652.                 /* must be open for this to work */
  653.     if ((state == OPEN) && stream && stream->dtb && (s = stream->dtb->name) &&
  654.     (!strcmp (s,"bezerk") || !strcmp (s,"mbox") || !strcmp (s,"mmdf"))) {
  655.       fputs("* OK [READ-ONLY] Now READ-ONLY, mailbox lock surrendered\015\012",
  656.         stdout);
  657.       stream->rdonly = T;    /* make the stream readonly */
  658.       mail_ping (stream);    /* cause it to stick! */
  659.     }
  660.     else fputs ("* NO Unexpected KOD interrupt received, ignored\015\012",
  661.         stdout);
  662.     fflush (stdout);        /* make sure output blatted */
  663.   }
  664. }
  665.  
  666. /* Slurp a command line
  667.  * Accepts: buffer pointer
  668.  *        buffer size
  669.  */
  670.  
  671. void slurp (char *s,int n)
  672. {
  673.   alarm (TIMEOUT);        /* get a command under timeout */
  674.   errno = 0;            /* clear error */
  675.   while (!fgets (s,n,stdin)) {    /* read buffer */
  676.     if (errno==EINTR) errno = 0;/* ignore if some interrupt */
  677.     else {
  678.       syslog (LOG_INFO,
  679.           "Connection broken while reading line user=%.80s host=%.80s",
  680.           user ? user : "???",tcp_clienthost (s));
  681.       _exit (1);
  682.     }
  683.   }
  684.   alarm (0);            /* make sure timeout disabled */
  685. }
  686.  
  687.  
  688. /* Read a character
  689.  * Returns: character
  690.  */
  691.  
  692. char inchar ()
  693. {
  694.   char c,tmp[MAILTMPLEN];
  695.   while ((c = getchar ()) == EOF) {
  696.     if (errno==EINTR) errno = 0;/* ignore if some interrupt */
  697.     else {
  698.       syslog (LOG_INFO,
  699.           "Connection broken while reading char user=%.80s host=%.80s",
  700.           user ? user : "???",tcp_clienthost (tmp));
  701.       _exit (1);
  702.     }
  703.   }
  704.   return c;
  705. }
  706.  
  707.  
  708. /* Flush until newline seen
  709.  * Returns: NIL, always
  710.  */
  711.  
  712. void *flush ()
  713. {
  714.   while (inchar () != '\012');    /* slurp until we find newline */
  715.   response = toobig;
  716.   return NIL;
  717. }
  718.  
  719. /* Snarf an argument
  720.  * Accepts: pointer to argument text pointer
  721.  * Returns: argument
  722.  */
  723.  
  724. char *snarf (char **arg)
  725. {
  726.   long i;
  727.   char tmp[TMPLEN];
  728.   char *c = *arg;
  729.   char *s = c + 1;
  730.   char *t = NIL;
  731.   if (!c) return NIL;        /* better be an argument */
  732.   switch (*c) {            /* see what the argument is */
  733.   case '\0':            /* catch bogons */
  734.   case ' ':
  735.     return NIL;
  736.   case '"':            /* quoted string */
  737.     for (c = s; *c != '"'; c++){/* hunt for trailing quote */
  738.       if (*c == '\\') c++;    /* quote next character */
  739.       if (!*c) return NIL;    /* unterminated quoted string! */
  740.     }
  741.     *c++ = '\0';        /* tie off string */
  742.                 /* update argument pointer */
  743.     if (*c) *arg = (*c == ' ') ? c + 1 : c;
  744.     else *arg = NIL;        /* no more arguments */
  745.     return s;
  746.   case '{':            /* literal string */
  747.     if (isdigit (*s)) {        /* be sure about that */
  748.       i = strtol (s,&t,10);    /* get its length */
  749.                 /* validate end of literal */
  750.       if (*t++ != '}' || *t++) return NIL;
  751.       fputs (argrdy,stdout);    /* tell client ready for argument */
  752.       fflush (stdout);        /* dump output buffer */
  753.                 /* get a literal buffer */
  754.       c = litbuf = (char *) fs_get (i+1);
  755.       alarm (TIMEOUT);        /* start timeout */
  756.       while (i--) *c++ = inchar ();
  757.       alarm (0);        /* stop timeout */
  758.       *c++ = NIL;        /* make sure string tied off */
  759.       c = litbuf;        /* return value */
  760.                     /* get new command tail */
  761.       slurp ((*arg = t),TMPLEN - (t - cmdbuf) - 1);
  762.                 /* flush if didn't see NL */
  763.       if (!strchr (t,'\012')) return flush ();
  764.       break;
  765.     }
  766.                 /* otherwise fall through (third party IMAP) */
  767.   default:            /* atomic string */
  768.     c = strtok (c," \015\012");
  769.     break;
  770.   }
  771.                 /* remainder of arguments */
  772.   if ((*arg = strtok (t,"\015\012")) && **arg == ' ') ++*arg;
  773.   return c;
  774. }
  775.  
  776. /* Fetch message data
  777.  * Accepts: sequence as a string
  778.  *        string of data items to be fetched
  779.  */
  780.  
  781. #define MAXFETCH 100
  782.  
  783. void fetch (char *s,char *t)
  784. {
  785.   char c,*v;
  786.   long i,k;
  787.   BODY *b;
  788.   int parse_envs = NIL;
  789.   int parse_bodies = NIL;
  790.   void (*f[MAXFETCH]) ();
  791.   char *fa[MAXFETCH];
  792.                 /* c-client clobbers sequence, use spare */
  793.   if (mail_sequence (stream,s)) for (i = 1; i <= nmsgs; i++)
  794.     mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
  795.   else {            /* sequence bogus */
  796.     response = badseq;        /* punt */
  797.     return;
  798.   }
  799.                 /* process macros */
  800.   if (!strcmp (ucase (t),"ALL"))
  801.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  802.   else if (!strcmp (t,"FULL"))
  803.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  804.   else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  805.   s = t + strlen (t) - 1;    /* last character in attribute string */
  806.                 /* if multiple items, make sure in list form */
  807.   if (strchr (t,' ') && ((*t != '(') || (*s != ')'))) {
  808.     response = badatt;
  809.     return;
  810.   }
  811.                 /* nuke the parens now */
  812.   if ((*t == '(') && (*s == ')') && t++) *s = '\0';
  813.   k = 0;            /* initial index */
  814.   if (s = strtok (t," ")) do {    /* parse attribute list */
  815.     if (*s == 'B' && s[1] == 'O' && s[2] == 'D' && s[3] == 'Y') {
  816.                 /* we will need to parse bodies */
  817.       parse_envs = parse_bodies = T;
  818.       switch (s[4]) {
  819.       case '\0':        /* entire body */
  820.     f[k++] = fetch_body;
  821.     break;
  822.       case '[':            /* body segment */
  823.     if ((v = strchr (s + 5,']')) && (!(*v = v[1]))) {
  824.       fa[k] = s + 5;    /* set argument */
  825.       f[k++] = fetch_body_part;
  826.       break;        /* valid body segment */
  827.     }            /* else fall into default case */
  828.       default:            /* bogus */
  829.     response = badatt;
  830.     return;
  831.       }
  832.     }
  833.     else if (!strcmp (s,"ENVELOPE")) {
  834.       parse_envs = T;        /* we will need to parse envelopes */
  835.       f[k++] = fetch_envelope;
  836.     }
  837.     else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  838.     else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  839.     else if (!strcmp (s,"RFC822")) f[k++] = fetch_rfc822;
  840.     else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  841.     else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  842.     else if (!strcmp (s,"RFC822.TEXT")) f[k++] = fetch_rfc822_text;
  843.     else {            /* unknown attribute */
  844.       response = badatt;
  845.       return;
  846.     }
  847.   } while ((s = strtok (NIL," ")) && k < MAXFETCH);
  848.   else {
  849.     response = misarg;        /* missing attribute list */
  850.     return;
  851.   }
  852.   if (s) {            /* too many attributes? */
  853.     response = "%s BAD Excessively complex FETCH attribute list\015\012";
  854.     return;
  855.   }
  856.   f[k++] = NIL;            /* tie off attribute list */
  857.                 /* for each requested message */
  858.   for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare) {
  859.                 /* parse envelope, set body, do warnings */
  860.     if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
  861.     printf ("* %d FETCH (",i);    /* leader */
  862.     (*f[0]) (i,fa[0]);        /* do first attribute */
  863.     for (k = 1; f[k]; k++) {    /* for each subsequent attribute */
  864.       putchar (' ');        /* delimit with space */
  865.       (*f[k]) (i,fa[k]);    /* do that attribute */
  866.     }
  867.     fputs (")\015\012",stdout);    /* trailer */
  868.   }
  869. }
  870.  
  871. /* Fetch message body structure
  872.  * Accepts: message number
  873.  *        string argument
  874.  */
  875.  
  876.  
  877. void fetch_body (long i,char *s)
  878. {
  879.   BODY *body;
  880.   mail_fetchstructure (stream,i,&body);
  881.   fputs ("BODY ",stdout);    /* output attribute */
  882.   pbody (body);            /* output body */
  883. }
  884.  
  885.  
  886. /* Fetch message body part
  887.  * Accepts: message number
  888.  *        string argument
  889.  */
  890.  
  891. void fetch_body_part (long i,char *s)
  892. {
  893.   unsigned long j;
  894.   long k = 0;
  895.   BODY *body;
  896.   int f = mail_elt (stream,i)->seen;
  897.   mail_fetchstructure (stream,i,&body);
  898.   printf ("BODY[%s] ",s);    /* output attribute */
  899.   if (body && (s = mail_fetchbody (stream,i,s,&j))) {
  900.     printf ("{%d}\015\012",j);    /* and literal string */
  901.     while (j -= k) k = fwrite (s += k,1,j,stdout);
  902.     changed_flags (i,f);    /* output changed flags */
  903.   }
  904.   else fputs ("NIL",stdout);    /* can't output anything at all */
  905. }
  906.  
  907. /* Fetch IMAP envelope
  908.  * Accepts: message number
  909.  *        string argument
  910.  */
  911.  
  912. void fetch_envelope (long i,char *s)
  913. {
  914.   ENVELOPE *env = mail_fetchstructure (stream,i,NIL);
  915.   fputs ("ENVELOPE ",stdout);    /* output attribute */
  916.   penv (env);            /* output envelope */
  917. }
  918.  
  919. /* Fetch flags
  920.  * Accepts: message number
  921.  *        string argument
  922.  */
  923.  
  924. void fetch_flags (long i,char *s)
  925. {
  926.   char tmp[MAILTMPLEN];
  927.   unsigned long u;
  928.   char *t;
  929.   MESSAGECACHE *elt = mail_elt (stream,i);
  930.   if (!elt->valid) {        /* have valid flags yet? */
  931.     sprintf (tmp,"%ld",i);
  932.     mail_fetchflags (stream,tmp);
  933.   }
  934.   s = tmp;
  935.   s[0] = s[1] = '\0';        /* start with empty flag string */
  936.                 /* output system flags */
  937.   if (elt->recent) strcat (s," \\Recent");
  938.   if (elt->seen) strcat (s," \\Seen");
  939.   if (elt->deleted) strcat (s," \\Deleted");
  940.   if (elt->flagged) strcat (s," \\Flagged");
  941.   if (elt->answered) strcat (s," \\Answered");
  942.   if (u = elt->user_flags) do    /* any user flags? */
  943.     if ((TMPLEN - ((s += strlen (s)) - tmp)) >
  944.     (2 + strlen (t = stream->user_flags[find_rightmost_bit (&u)]))) {
  945.       *s++ = ' ';        /* space delimiter */
  946.       strcpy (s,t);        /* copy the user flag */
  947.     }
  948.   while (u);            /* until no more user flags */
  949.   printf ("FLAGS (%s)",tmp+1);    /* output results, skip first char of list */
  950.   elt->spare2 = NIL;        /* we've sent the update */
  951. }
  952.  
  953.  
  954. /* Output flags if was unseen
  955.  * Accepts: message number
  956.  *        prior value of Seen flag
  957.  */
  958.  
  959. void changed_flags (long i,int f)
  960. {
  961.   if (!f) {            /* was unseen? */
  962.     putchar (' ');        /* yes, delimit with space */
  963.     fetch_flags (i,NIL);    /* output flags */
  964.   }
  965. }
  966.  
  967. /* Fetch message internal date
  968.  * Accepts: message number
  969.  *        string argument
  970.  */
  971.  
  972. void fetch_internaldate (long i,char *s)
  973. {
  974.   char tmp[MAILTMPLEN];
  975.   MESSAGECACHE *elt = mail_elt (stream,i);
  976.   if (!elt->day) {        /* have internal date yet? */
  977.     sprintf (tmp,"%ld",i);
  978.     mail_fetchfast (stream,tmp);
  979.   }
  980.   printf ("INTERNALDATE \"%s\"",mail_date (tmp,elt));
  981. }
  982.  
  983.  
  984. /* Fetch complete RFC-822 format message
  985.  * Accepts: message number
  986.  *        string argument
  987.  */
  988.  
  989. void fetch_rfc822 (long i,char *s)
  990. {
  991.   int f = mail_elt (stream,i)->seen;
  992.   printf ("RFC822 {%d}\015\012",mail_elt (stream,i)->rfc822_size);
  993.   fputs (mail_fetchheader (stream,i),stdout);
  994.   fputs (mail_fetchtext (stream,i),stdout);
  995.   changed_flags (i,f);        /* output changed flags */
  996. }
  997.  
  998.  
  999. /* Fetch RFC-822 header
  1000.  * Accepts: message number
  1001.  *        string argument
  1002.  */
  1003.  
  1004. void fetch_rfc822_header (long i,char *s)
  1005. {
  1006.   fputs ("RFC822.HEADER ",stdout);
  1007.   pstring (mail_fetchheader (stream,i));
  1008. }
  1009.  
  1010. /* Fetch RFC-822 message length
  1011.  * Accepts: message number
  1012.  *        string argument
  1013.  */
  1014.  
  1015. void fetch_rfc822_size (long i,char *s)
  1016. {
  1017.   MESSAGECACHE *elt = mail_elt (stream,i);
  1018.   if (!elt->rfc822_size) {    /* have message size yet? */
  1019.     char tmp[MAILTMPLEN];
  1020.     sprintf (tmp,"%ld",i);
  1021.     mail_fetchfast (stream,tmp);
  1022.   }
  1023.   printf ("RFC822.SIZE %d",elt->rfc822_size);
  1024. }
  1025.  
  1026.  
  1027. /* Fetch RFC-822 text only
  1028.  * Accepts: message number
  1029.  *        string argument
  1030.  */
  1031.  
  1032. void fetch_rfc822_text (long i,char *s)
  1033. {
  1034.   int f = mail_elt (stream,i)->seen;
  1035.   fputs ("RFC822.TEXT ",stdout);
  1036.   pstring (mail_fetchtext (stream,i));
  1037.   changed_flags (i,f);        /* output changed flags */
  1038. }
  1039.  
  1040. /* Print envelope
  1041.  * Accepts: body
  1042.  */
  1043.  
  1044. void penv (ENVELOPE *env)
  1045. {
  1046.   if (env) {            /* only if there is an envelope */
  1047.                 /* disgusting MacMS kludge */
  1048.     if (mackludge) printf ("%d ",cstring (env->date) + cstring (env->subject) +
  1049.                caddr (env->from) + caddr (env->sender) +
  1050.                caddr (env->reply_to) + caddr (env->to) +
  1051.                caddr (env->cc) + caddr (env->bcc) +
  1052.                cstring (env->in_reply_to) +
  1053.                cstring (env->message_id));
  1054.     putchar ('(');        /* delimiter */
  1055.     pstring (env->date);    /* output envelope fields */
  1056.     putchar (' ');
  1057.     pstring (env->subject);
  1058.     putchar (' ');
  1059.     paddr (env->from);
  1060.     putchar (' ');
  1061.     paddr (env->sender);
  1062.     putchar (' ');
  1063.     paddr (env->reply_to);
  1064.     putchar (' ');
  1065.     paddr (env->to);
  1066.     putchar (' ');
  1067.     paddr (env->cc);
  1068.     putchar (' ');
  1069.     paddr (env->bcc);
  1070.     putchar (' ');
  1071.     pstring (env->in_reply_to);
  1072.     putchar (' ');
  1073.     pstring (env->message_id);
  1074.     putchar (')');        /* end of envelope */
  1075.   }
  1076.   else fputs ("NIL",stdout);    /* no envelope */
  1077. }
  1078.  
  1079. /* Print body
  1080.  * Accepts: body
  1081.  */
  1082.  
  1083. void pbody (BODY *body)
  1084. {
  1085.   if (body) {            /* only if there is a body */
  1086.     PARAMETER *param;
  1087.     PART *part;
  1088.     putchar ('(');        /* delimiter */
  1089.                 /* multipart type? */
  1090.     if (body->type == TYPEMULTIPART) {
  1091.                 /* print each part */
  1092.       if (part = body->contents.part)
  1093.     for (; part; part = part->next) pbody (&(part->body));
  1094.       else fputs ("(\"TEXT\" \"PLAIN\" NIL NIL NIL \"7BIT\" 0 0)",stdout);
  1095.       putchar (' ');        /* space delimiter */
  1096.       pstring (body->subtype);    /* and finally the subtype */
  1097.     }
  1098.     else {            /* non-multipart body type */
  1099.       pstring ((char *) body_types[body->type]);
  1100.       putchar (' ');
  1101.       pstring (body->subtype);
  1102.       if (param = body->parameter) {
  1103.     fputs (" (",stdout);
  1104.     do {
  1105.       pstring (param->attribute);
  1106.       putchar (' ');
  1107.       pstring (param->value);
  1108.       if (param = param->next) putchar (' ');
  1109.     } while (param);
  1110.     fputs (") ",stdout);
  1111.       }
  1112.       else fputs (" NIL ",stdout);
  1113.       pstring (body->id);
  1114.       putchar (' ');
  1115.       pstring (body->description);
  1116.       putchar (' ');
  1117.       pstring ((char *) body_encodings[body->encoding]);
  1118.       printf (" %d",body->size.bytes);
  1119.       switch (body->type) {    /* extra stuff depends upon body type */
  1120.       case TYPEMESSAGE:
  1121.                 /* can't do this if not RFC822 */
  1122.     if (strcmp (body->subtype,"RFC822")) break;
  1123.     putchar (' ');
  1124.     penv (body->contents.msg.env);
  1125.     putchar (' ');
  1126.     pbody (body->contents.msg.body);
  1127.       case TYPETEXT:
  1128.     printf (" %d",body->size.lines);
  1129.     break;
  1130.       default:
  1131.     break;
  1132.       }
  1133.     }
  1134.     putchar (')');        /* end of body */
  1135.   }
  1136.   else fputs ("NIL",stdout);    /* no body */
  1137. }
  1138.  
  1139. /* Print string
  1140.  * Accepts: string
  1141.  */
  1142.  
  1143. void pstring (char *s)
  1144. {
  1145.   char c,*t;
  1146.   if (s) {            /* is there a string? */
  1147.                 /* must use literal string */
  1148.     if (strpbrk (s,"\012\015\"%{\\")) {
  1149.       printf ("{%d}\015\012",strlen (s));
  1150.       fputs (s,stdout);        /* don't merge this with the printf() */
  1151.     }
  1152.     else {            /* may use quoted string */
  1153.       putchar ('"');        /* don't even think of merging this into a */
  1154.       fputs (s,stdout);        /*  printf().  Cretin VAXen can't do a */
  1155.       putchar ('"');        /*  printf() of godzilla strings! */
  1156.     }
  1157.   }
  1158.   else fputs ("NIL",stdout);    /* empty string */
  1159. }
  1160.  
  1161.  
  1162. /* Print address list
  1163.  * Accepts: address list
  1164.  */
  1165.  
  1166. void paddr (ADDRESS *a)
  1167. {
  1168.   if (a) {            /* have anything in address? */
  1169.     putchar ('(');        /* open the address list */
  1170.     do {            /* for each address */
  1171.       putchar ('(');        /* open the address */
  1172.       pstring (a->personal);    /* personal name */
  1173.       putchar (' ');
  1174.       pstring (a->adl);        /* at-domain-list */
  1175.       putchar (' ');
  1176.       pstring (a->mailbox);    /* mailbox */
  1177.       putchar (' ');
  1178.       pstring (a->host);    /* domain name of mailbox's host */
  1179.       putchar (')');        /* terminate address */
  1180.     } while (a = a->next);    /* until end of address */
  1181.     putchar (')');        /* close address list */
  1182.   }
  1183.   else fputs ("NIL",stdout);    /* empty address */
  1184. }
  1185.  
  1186. /* Count string and space afterwards
  1187.  * Accepts: string
  1188.  * Returns: 1 plus length of string
  1189.  */
  1190.  
  1191. long cstring (char *s)
  1192. {
  1193.   char tmp[MAILTMPLEN];
  1194.   long i = s ? strlen (s) : 0;
  1195.   if (s) {            /* is there a string? */
  1196.                 /* must use literal string */
  1197.     if (strpbrk (s,"\012\015\"%{\\")) {
  1198.       sprintf (tmp,"{%d}\015\012",i);
  1199.       i += strlen (tmp);
  1200.     }
  1201.     else i += 2;        /* quoted string */
  1202.   }
  1203.   else i += 3;            /* NIL */
  1204.   return i + 1;            /* return string plus trailing space */
  1205. }
  1206.  
  1207.  
  1208. /* Count address list and space afterwards
  1209.  * Accepts: address list
  1210.  */
  1211.  
  1212. long caddr (ADDRESS *a)
  1213. {
  1214.   long i = 3;            /* open, close, and space */
  1215.                 /* count strings in address list */
  1216.   if (a) do i += 1 + cstring (a->personal) + cstring (a->adl) +
  1217.     cstring (a->mailbox) + cstring (a->host);
  1218.   while (a = a->next);        /* until end of address */
  1219.   else i = 4;            /* NIL plus space */
  1220.   return i;            /* return the count */
  1221. }
  1222.  
  1223. /* Co-routines from MAIL library */
  1224.  
  1225.  
  1226. /* Message matches a search
  1227.  * Accepts: MAIL stream
  1228.  *        message number
  1229.  */
  1230.  
  1231. void mm_searched (MAILSTREAM *s,long msgno)
  1232. {
  1233.                 /* nothing to do here */
  1234. }
  1235.  
  1236.  
  1237. /* Message exists (i.e. there are that many messages in the mailbox)
  1238.  * Accepts: MAIL stream
  1239.  *        message number
  1240.  */
  1241.  
  1242. void mm_exists (MAILSTREAM *s,long number)
  1243. {
  1244.   if (s != tstream) {        /* note change in number of messages */
  1245.     printf ("* %d EXISTS\015\012",(nmsgs = number));
  1246.     recent = -1;        /* make sure fetch new recent count */
  1247.   }
  1248. }
  1249.  
  1250.  
  1251. /* Message expunged
  1252.  * Accepts: MAIL stream
  1253.  *        message number
  1254.  */
  1255.  
  1256. void mm_expunged (MAILSTREAM *s,long number)
  1257. {
  1258.   if (s != tstream) printf ("* %d EXPUNGE\015\012",number);
  1259. }
  1260.  
  1261.  
  1262. /* Message status changed
  1263.  * Accepts: MAIL stream
  1264.  *        message number
  1265.  */
  1266.  
  1267. void mm_flags (MAILSTREAM *s,long number)
  1268. {
  1269.   if (s != tstream) mail_elt (s,number)->spare2 = T;
  1270. }
  1271.  
  1272. /* Mailbox found
  1273.  * Accepts: Mailbox name
  1274.  */
  1275.  
  1276. void mm_mailbox (char *string)
  1277. {
  1278.   printf ("* MAILBOX %s\015\012",string);
  1279. }
  1280.  
  1281.  
  1282. /* BBoard found
  1283.  * Accepts: BBoard name
  1284.  */
  1285.  
  1286. void mm_bboard (char *string)
  1287. {
  1288.   printf ("* BBOARD %s\015\012",string);
  1289. }
  1290.  
  1291. /* Notification event
  1292.  * Accepts: MAIL stream
  1293.  *        string to log
  1294.  *        error flag
  1295.  */
  1296.  
  1297. void mm_notify (MAILSTREAM *s,char *string,long errflg)
  1298. {
  1299.   char tmp[MAILTMPLEN];
  1300.   if (!tstream || (s != tstream)) switch (errflg) {
  1301.   case NIL:            /* information message, set as OK response */
  1302.     tmp[11] = '\0';        /* see if TRYCREATE for MacMS kludge */
  1303.     if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
  1304.   case PARSE:            /* parse glitch, output unsolicited OK */
  1305.     printf ("* OK %s\015\012",string);
  1306.     break;
  1307.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1308.     printf ("* NO %s\015\012",string);
  1309.     break;
  1310.   case ERROR:            /* error that broke command */
  1311.   default:            /* default should never happen */
  1312.     printf ("* BAD %s\015\012",string);
  1313.     break;
  1314.   }
  1315. }
  1316.  
  1317. /* Log an event for the user to see
  1318.  * Accepts: string to log
  1319.  *        error flag
  1320.  */
  1321.  
  1322. void mm_log (char *string,long errflg)
  1323. {
  1324.   switch (errflg) {        /* action depends upon the error flag */
  1325.   case NIL:            /* information message, set as OK response */
  1326.     if (response == win) {    /* only if no other response yet */
  1327.       response = altwin;    /* switch to alternative win message */
  1328.       if (lsterr) fs_give ((void **) &lsterr);
  1329.       lsterr = cpystr (string);    /* copy string for later use */
  1330.     }
  1331.     break;
  1332.   case PARSE:            /* parse glitch, output unsolicited OK */
  1333.     printf ("* OK [PARSE] %s\015\012",string);
  1334.     break;
  1335.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1336.     if (strcmp (string,"Mailbox is empty")) printf ("* NO %s\015\012",string);
  1337.     break;
  1338.   case ERROR:            /* error that broke command */
  1339.   default:            /* default should never happen */
  1340.     response = lose;        /* set fatality */
  1341.     if (lsterr) fs_give ((void **) &lsterr);
  1342.     lsterr = cpystr (string);    /* note last error */
  1343.     break;
  1344.   }
  1345. }
  1346.  
  1347.  
  1348. /* Log an event to debugging telemetry
  1349.  * Accepts: string to log
  1350.  */
  1351.  
  1352. void mm_dlog (char *string)
  1353. {
  1354.   mm_log (string,WARN);        /* shouldn't happen normally */
  1355. }
  1356.  
  1357. /* Get user name and password for this host
  1358.  * Accepts: host name
  1359.  *        where to return user name
  1360.  *        where to return password
  1361.  *        trial count
  1362.  */
  1363.  
  1364. void mm_login (char *host,char *username,char *password,long trial)
  1365. {
  1366.   strcpy (username,user);    /* set user name */
  1367.   strcpy (password,pass);    /* and password */
  1368. }
  1369.  
  1370. /* About to enter critical code
  1371.  * Accepts: stream
  1372.  */
  1373.  
  1374. void mm_critical (MAILSTREAM *s)
  1375. {
  1376.   /* Not doing anything here for now */
  1377. }
  1378.  
  1379.  
  1380. /* About to exit critical code
  1381.  * Accepts: stream
  1382.  */
  1383.  
  1384. void mm_nocritical (MAILSTREAM *s)
  1385. {
  1386.   /* Not doing anything here for now */
  1387. }
  1388.  
  1389.  
  1390. /* Disk error found
  1391.  * Accepts: stream
  1392.  *        system error code
  1393.  *        flag indicating that mailbox may be clobbered
  1394.  * Returns: abort flag
  1395.  */
  1396.  
  1397. long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  1398. {
  1399.   char tmp[MAILTMPLEN];
  1400.   if (serious) {        /* try your damnest if clobberage likely */
  1401.     fputs ("* BAD Retrying to fix probable mailbox damage!\015\012",stdout);
  1402.     fflush (stdout);        /* dump output buffer */
  1403.     syslog (LOG_ALERT,
  1404.         "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1405.         user,tcp_clienthost (tmp),
  1406.         (stream && stream->mailbox) ? stream->mailbox : "???",
  1407.         strerror (errcode));
  1408.     alarm (0);            /* make damn sure timeout disabled */
  1409.     sleep (60);            /* give it some time to clear up */
  1410.     return NIL;
  1411.   }
  1412.                 /* otherwise die before more damage is done */
  1413.   printf ("* BYE Aborting due to disk error %s\015\012",strerror (errcode));
  1414.   syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1415.       user,tcp_clienthost (tmp),
  1416.       (stream && stream->mailbox) ? stream->mailbox : "???",
  1417.       strerror (errcode));
  1418.   return T;
  1419. }
  1420.  
  1421.  
  1422. /* Log a fatal error event
  1423.  * Accepts: string to log
  1424.  */
  1425.  
  1426. void mm_fatal (char *string)
  1427. {
  1428.   char tmp[MAILTMPLEN];
  1429.   printf ("* BYE [ALERT] IMAP2bis server crashing: %s\015\012",string);
  1430.   syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1431.       user ? user : "???",tcp_clienthost (tmp),
  1432.       (stream && stream->mailbox) ? stream->mailbox : "???",string);
  1433. }
  1434.